package com.dragon.cloud.zconf.client;

import com.dragon.cloud.zconf.curator.CuratorClient;
import com.dragon.cloud.zconf.util.PropertyHelper;
import com.dragon.cloud.zconf.util.ZConfConstant;
import com.dragon.cloud.zconf.util.ZConfNodeUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.curator.framework.CuratorFramework;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

public class ZConfClientImpl implements ZConfClient {
    private static Logger logger = LoggerFactory.getLogger(ZConfClientImpl.class);
    private static ZConfClientImpl instance = null;
    public String appId = null;
    CuratorFramework curatorClient = null;
    private ConcurrentHashMap<String, ZConfNode> zConfNodes = new ConcurrentHashMap<String, ZConfNode>();
    private Object obj = new Object();

    private ZConfClientImpl() {
        // design app code and secret key for zookeeper acl,
        // but it does not develop.use it for Permissions identification.
        this.appId = PropertyHelper.getSystemOrEnvProperty("dragon.cloud.zconf.appId");
        String zkServer = PropertyHelper.getSystemOrEnvProperty("dragon.cloud.zconf.zkServer");
        try {
            curatorClient = CuratorClient.getInstance(zkServer).getCuratorFramework();
            curatorClient.blockUntilConnected(100, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            logger.error("ZConfClientImpl init failed. " + e.getMessage(), e);
        }
    }

    private ZConfClientImpl(String appId, String zkServer) {
        // design app code and secret key for zookeeper acl,
        // but it does not develop.use it for Permissions identification.
        this.appId = appId;
        try {
            curatorClient = CuratorClient.getInstance(zkServer).getCuratorFramework();
            curatorClient.blockUntilConnected(100, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            logger.error("ZConfClientImpl init failed. " + e.getMessage(), e);
        }
    }

    public static ZConfClientImpl getInstance(String appId, String zkServer) {
        if (null != instance) {
            return instance;
        }
        synchronized (ZConfClientImpl.class) {
            if (null == instance) {
                instance = new ZConfClientImpl(appId, zkServer);
                logger.info("ZConfClientImpl instance createed successed.");
            }
            return instance;
        }
    }

    public static ZConfClientImpl getInstance() {
        if (null != instance) {
            return instance;
        }
        synchronized (ZConfClientImpl.class) {
            if (null == instance) {
                instance = new ZConfClientImpl();
                logger.info("ZConfClientImpl instance createed successed.");
            }
            return instance;
        }
    }

    @Override
    public ZConfNode getConfigByKey(String key) throws ZConfException {
        return getConfigByKey(this.appId, key);
    }

    @Override
    public boolean setConfigValue(String key, String value) throws ZConfException {
        if (value == null) {
            throw new ZConfException("illegal value,value can not be null!");
        }
        ZConfNode zConfNode = getConfigByKey(key);
        return zConfNode.setValue(value);
    }

    private ZConfNode getConfigByKey(String appId, String key) throws ZConfException {
        if (StringUtils.isEmpty(appId)) {
            logger.error("illegal appId: " + appId);
            throw new ZConfException("illegal appId: " + appId);
        }
        if (StringUtils.isEmpty(key)) {
            logger.error("illegal key: " + key);
            throw new ZConfException("illegal key: " + key);
        }
        return getConfigByPath(ZConfConstant.CONFIG_FILE_PATH + "/" + appId + "/" + key);
    }

    private ZConfNode getConfigByPath(String path) throws ZConfException {
        if (!ZConfNodeUtil.legalNodePath(path)) {
            logger.error("illegal path: " + path);
            throw new ZConfException("illegal path: " + path);
        }
        ZConfNode zConfNode = zConfNodes.get(path);
        if (null == zConfNode) {
            synchronized (obj) {
                zConfNode = zConfNodes.get(path);
                if (null == zConfNode) {
                    zConfNode = new ZConfNodeImpl(path, getCuratorClient());
                    zConfNodes.put(path, zConfNode);
                }
            }
        }
        return zConfNode;
    }

    private CuratorFramework getCuratorClient() {
        return curatorClient;
    }
}
